"""
@File         : service.py
@Time         : 2024/05/10 21:43:43
@Author       : Qrj
@Email        : 923207736@qq.com
@Description  : 站内信记录相关业务逻辑
"""

from datetime import datetime

from cashews import NOT_NONE
from sqlalchemy import and_, delete, func, insert, select, update
from sqlalchemy.ext.asyncio import AsyncSession

from src.business.admin.system.station_message.template.service import (
    StationMessageTemplateService,
)
from src.business.admin.system.user.models import User
from src.exceptions.custom_async_validator import generate_async_validator_exception
from src.schemas.validate import (
    OrderBySearchValidateSchema,
    PaginationSearchValidateSchema,
)
from src.utils.cashews import CashewsCondition, data_cache
from src.utils.string import StringReplaceUtil

from .models import StationMessageRecord
from .schemas.out import (
    ListStationMessageRecordCompleteOutSchema,
    StationMessageRecordCompleteOutSchema,
)
from .schemas.validate import (
    StationMessageRecordCreateValidateSchema,
    StationMessageRecordDeleteValidateSchema,
    StationMessageRecordListSearchValidateSchema,
    StationMessageRecordSelfUpdateReadStatusValidateSchema,
)


class StationMessageRecordService:
    """站内信记录业务逻辑类"""

    @data_cache(
        ttl="30m",
        key="station_message_record#id:{tenant_id}:{station_message_record_id}",
        tags=[
            "station_message_record#id:{tenant_id}",
            "station_message_record#id:{tenant_id}:{station_message_record_id}",
        ],
        condition=NOT_NONE,
    )
    @staticmethod
    async def get_station_message_record_use_id(
        db_session: AsyncSession,
        tenant_id: int,
        station_message_record_id: int,
        raise_not_exists_exception: bool = True,
    ) -> StationMessageRecordCompleteOutSchema | None:
        """获取站内信记录信息

        Args:
            db_session (AsyncSession): 数据库连接池
            tenant_id (int): 租户ID
            station_message_record_id (int): 站内信记录ID
            raise_not_exists_exception (bool, optional): 未找到时是否报错

        Returns:
            StationMessageRecordCompleteOutSchema: 站内信记录信息
        """
        search_sql = select(
            *StationMessageRecordCompleteOutSchema.convert_to_sql_model_field(
                StationMessageRecord
            )
        ).where(
            StationMessageRecord.id == station_message_record_id,
            StationMessageRecord.tenant_id == tenant_id,
        )
        async with db_session.begin():
            search_res = await db_session.execute(search_sql)
        result = search_res.mappings().first()
        if result is not None:
            # 转换成pydantic的序列化模型
            result = StationMessageRecordCompleteOutSchema.model_validate(result)
            return result
        elif result is None and raise_not_exists_exception:
            # 未找到则报错
            raise generate_async_validator_exception("id", "站内信记录不存在")
        else:
            return result

    @data_cache(
        ttl="3m",
        key="complete_station_message_record_list:{tenant_id}:{search_schema}:{pagination_schema}:{order_schema}",
        tags=["complete_station_message_record_list:{tenant_id}"],
        condition=CashewsCondition.condition_only_true_result,
    )
    @staticmethod
    async def list_search_station_message_record(
        db_session: AsyncSession,
        tenant_id: int,
        search_schema: StationMessageRecordListSearchValidateSchema,
        pagination_schema: PaginationSearchValidateSchema,
        order_schema: OrderBySearchValidateSchema,
        need_select_total: bool = True,
    ) -> tuple[list[StationMessageRecordCompleteOutSchema], int | None]:
        """指定租户的完整的站内信记录列表查询

        Args:
            db_session (AsyncSession): 数据库连接池
            tenant_id: 租户ID
            search_schema (MenuListSearchValidateSchema): 查询参数
            pagination_schema: 分页参数
            order_schema: 排序参数
            need_select_total: 是否需要查询总数

        Returns:
            tuple[list[StationMessageRecordCompleteOutSchema], int | None]: 查询结果、查询总数
        """
        # 构建排序字段
        order_by_field_sql = order_schema.convert_to_order_by_sql(
            search_schema, StationMessageRecord
        )
        # 构建查询sql
        list_search_sql = (
            select(
                *StationMessageRecordCompleteOutSchema.convert_to_sql_model_field(
                    StationMessageRecord
                )
            )
            .where(
                and_(*search_schema.convert_to_where_sql(StationMessageRecord)),
                StationMessageRecord.tenant_id == tenant_id,
            )
            .order_by(order_by_field_sql)
            .offset(pagination_schema.offset)
            .limit(pagination_schema.limit)
        )
        # 开始查询
        async with db_session.begin():
            list_search_res = await db_session.execute(list_search_sql)
        result = list_search_res.mappings().all()
        # 根据need_select_total参数来确定是否需要查询总数
        if need_select_total:
            total_sql = (
                select(func.count())
                .select_from(StationMessageRecord)
                .where(
                    and_(*search_schema.convert_to_where_sql(StationMessageRecord)),
                    StationMessageRecord.tenant_id == tenant_id,
                )
            )
            async with db_session.begin():
                total_res = await db_session.execute(total_sql)
            total = total_res.scalar()
        else:
            total = None
        # 统一转为pydantic列表模型
        return ListStationMessageRecordCompleteOutSchema.validate_python(result), total

    @data_cache(
        ttl="3m",
        key="user_self_station_message_record_list:{tenant_id}:{user_id}:{search_schema}:{pagination_schema}:{order_schema}",
        tags=["user_self_station_message_record_list:{tenant_id}:{user_id}"],
        condition=CashewsCondition.condition_only_true_result,
    )
    @staticmethod
    async def list_get_user_self_station_message_record(
        db_session: AsyncSession,
        tenant_id: int,
        user_id: int,
        search_schema: StationMessageRecordListSearchValidateSchema,
        pagination_schema: PaginationSearchValidateSchema,
        order_schema: OrderBySearchValidateSchema,
        need_select_total: bool = True,
    ) -> tuple[list[StationMessageRecordCompleteOutSchema], int | None]:
        """当前用户的完整的站内信记录列表查询

        Args:
            db_session (AsyncSession): 数据库连接池
            tenant_id: 租户ID
            user_id: 用户ID
            search_schema (MenuListSearchValidateSchema): 查询参数
            pagination_schema: 分页参数
            order_schema: 排序参数
            need_select_total: 是否需要查询总数

        Returns:
            tuple[list[StationMessageRecordCompleteOutSchema], int | None]: 查询结果、查询总数
        """
        # 构建排序字段
        order_by_field_sql = order_schema.convert_to_order_by_sql(
            search_schema, StationMessageRecord
        )
        # 构建查询sql
        list_search_sql = (
            select(
                *StationMessageRecordCompleteOutSchema.convert_to_sql_model_field(
                    StationMessageRecord
                )
            )
            .where(
                and_(*search_schema.convert_to_where_sql(StationMessageRecord)),
                StationMessageRecord.tenant_id == tenant_id,
                StationMessageRecord.receiver_id == user_id,
            )
            .order_by(order_by_field_sql)
            .offset(pagination_schema.offset)
            .limit(pagination_schema.limit)
        )
        # 开始查询
        async with db_session.begin():
            list_search_res = await db_session.execute(list_search_sql)
        result = list_search_res.mappings().all()
        # 根据need_select_total参数来确定是否需要查询总数
        if need_select_total:
            total_sql = (
                select(func.count())
                .select_from(StationMessageRecord)
                .where(
                    and_(*search_schema.convert_to_where_sql(StationMessageRecord)),
                    StationMessageRecord.tenant_id == tenant_id,
                    StationMessageRecord.receiver_id == user_id,
                )
            )
            async with db_session.begin():
                total_res = await db_session.execute(total_sql)
            total = total_res.scalar()
        else:
            total = None
        # 统一转为pydantic列表模型
        return ListStationMessageRecordCompleteOutSchema.validate_python(result), total

    @data_cache(
        ttl="3m",
        key="user_self_unread_station_message_record_count:{tenant_id}:{user_id}",
        tags=["user_self_unread_station_message_record_count:{tenant_id}:{user_id}"],
    )
    @staticmethod
    async def get_user_self_unread_station_message_record_count(
        db_session: AsyncSession, tenant_id: int, user_id: int
    ) -> int:
        """查询当前用户未读的站内信记录数量

        Args:
            db_session (AsyncSession): 数据库连接池
            tenant_id: 租户ID
            user_id: 用户ID

        Returns:
            int: 查询总数
        """
        total_sql = (
            select(func.count())
            .select_from(StationMessageRecord)
            .where(
                StationMessageRecord.tenant_id == tenant_id,
                StationMessageRecord.receiver_id == user_id,
                StationMessageRecord.read_status.is_(False),
            )
        )
        async with db_session.begin():
            total_res = await db_session.execute(total_sql)
        total: int = total_res.scalar()
        return total

    @staticmethod
    @data_cache.transaction()
    async def create_station_message_record(
        db_session: AsyncSession,
        tenant_id: int,
        create_schema: StationMessageRecordCreateValidateSchema,
        creator_username: str = "",
    ):
        """创建站内信记录

        Args:
            db_session (AsyncSession): 数据库连接池
            tenant_id (int): 租户ID
            create_schema (StationMessageRecordCreateValidateSchema): 创建所需参数
            creator_username: 创建者用户名
        """
        # 只能发送站内信给相同租户的用户
        # 查找模板是否存在(已禁用也会报错)
        template = (
            await StationMessageTemplateService.get_station_message_template_use_id(
                db_session, create_schema.template_id
            )
        )
        # 查找接收用户
        select_user_sql = select(User.id, User.username).where(
            User.id.in_(create_schema.receiver_ids), User.tenant_id == tenant_id
        )
        async with db_session.begin():
            select_user_res = await db_session.execute(select_user_sql)
        select_user_mappings = select_user_res.mappings().all()
        if not select_user_mappings:
            raise generate_async_validator_exception("receiver_ids", "接收用户不存在")
        # 根据模板的内容生成消息的内容
        record_content, template_params = StringReplaceUtil.replace_string_var(
            template.content, template.params, create_schema.template_params
        )
        # 批量新增的sql
        insert_batch_dict_list = [
            {
                "content": record_content,
                "type": template.type,
                "template_params": template_params,
                "sending_datetime": datetime.now(),
                "sender_username": creator_username,
                "receiver_id": receiver_user["id"],
                "receiver_username": receiver_user["username"],
                "read_status": False,
                "tenant_id": tenant_id,
                "template_id": template.id,
                "template_name": template.name,
            }
            for receiver_user in select_user_mappings
        ]
        insert_sql = (insert(StationMessageRecord), insert_batch_dict_list)
        async with db_session.begin():
            await db_session.execute(insert_sql)
            # 清空相关缓存标签
            await data_cache.delete_tags(
                *[
                    f"user_self_station_message_record_list:{tenant_id}:{select_user['id']}"
                    for select_user in select_user_mappings
                ],
                *[
                    f"user_self_unread_station_message_record_count:{tenant_id}:{select_user['id']}"
                    for select_user in select_user_mappings
                ],
                f"complete_station_message_record_list:{tenant_id}",
            )

    @staticmethod
    @data_cache.transaction()
    async def update_self_station_message_record_read_status(
        db_session: AsyncSession,
        tenant_id: int,
        user_id: int,
        update_schema: StationMessageRecordSelfUpdateReadStatusValidateSchema,
    ):
        """修改用户自身站内信记录的已读状态

        Args:
            db_session (AsyncSession): 数据库连接池
            tenant_id (int): 租户ID
            user_id (int): 用户ID
            update_schema (StationMessageRecordSelfUpdateReadStatusValidateSchema): 修改操作所需的数据
        """
        # 查找需操作的站内信记录
        # 构建查询sql
        select_sql = select(
            StationMessageRecord.id, StationMessageRecord.receiver_id
        ).where(
            StationMessageRecord.id.in_(update_schema.ids),
            StationMessageRecord.tenant_id == tenant_id,
            StationMessageRecord.receiver_id == user_id,
        )
        # 进行sql查询
        async with db_session.begin():
            select_res = await db_session.execute(select_sql)
        select_result = select_res.mappings().all()
        # 进行修改操作
        if select_result:
            # 获取需操作的id列表
            id_list = [i["id"] for i in select_result]
            # 操作sql
            operate_sql = (
                update(StationMessageRecord)
                .values(
                    read_status=update_schema.read_status, read_datetime=datetime.now()
                )
                .where(StationMessageRecord.id.in_(id_list))
            )
            # 操作
            async with db_session.begin():
                await db_session.execute(operate_sql)
                delete_cache_tags = [
                    *[
                        f"station_message_record#id:{tenant_id}:{i['id']}"
                        for i in select_result
                    ],
                    *[
                        f"user_self_station_message_record_list:{tenant_id}:{i['receiver_id']}"
                        for i in select_result
                    ],
                    *[
                        f"user_self_unread_station_message_record_count:{tenant_id}:{i['receiver_id']}"
                        for i in select_result
                    ],
                    f"complete_station_message_record_list:{tenant_id}",
                ]
                # 清空相关缓存标签
                await data_cache.delete_tags(*delete_cache_tags)

    @staticmethod
    @data_cache.transaction()
    async def delete_station_message_record(
        db_session: AsyncSession,
        tenant_id: int,
        delete_schema: StationMessageRecordDeleteValidateSchema,
    ):
        """删除站内信记录

        Args:
            db_session (AsyncSession): 数据库连接池
            tenant_id: 租户ID
            delete_schema (StationMessageRecordDeleteValidateSchema): 删除操作所需的数据
        """
        # 查找需删除的站内信记录
        # 构建查询sql
        select_sql = select(
            StationMessageRecord.id, StationMessageRecord.receiver_id
        ).where(
            StationMessageRecord.id.in_(delete_schema.ids),
            StationMessageRecord.tenant_id == tenant_id,
        )
        # 进行sql查询
        async with db_session.begin():
            select_res = await db_session.execute(select_sql)
        select_result = select_res.mappings().all()
        # 进行删除操作
        if select_result:
            # 获取需删除的id列表
            delete_ids = [i["id"] for i in select_result]
            # 删除sql
            delete_sql = delete(StationMessageRecord).where(
                StationMessageRecord.id.in_(delete_ids)
            )
            # 删除操作
            async with db_session.begin():
                # 删除sql中的数据
                await db_session.execute(delete_sql)
                delete_cache_tags = [
                    *[
                        f"station_message_record#id:{tenant_id}:{i['id']}"
                        for i in select_result
                    ],
                    *[
                        f"user_self_station_message_record_list:{tenant_id}:{i['receiver_id']}"
                        for i in select_result
                    ],
                    *[
                        f"user_self_unread_station_message_record_count:{tenant_id}:{i['receiver_id']}"
                        for i in select_result
                    ],
                    f"complete_station_message_record_list:{tenant_id}",
                ]
                # 清空相关缓存标签
                await data_cache.delete_tags(*delete_cache_tags)
